from functools import reduce
import logging
logging.basicConfig()#level=logging.CRITICAL)

bit_chars = ('0','1','2','3','4','5','6','7')
bit_dict=({0,1},\
        {0,1,2,4,5,6},{0,1,2,4,5,6},\
        {0,1,2,3,4,5,6,7},\
        {0,1,2},{0,1,2},{0,1,2},{0,1,2})
bit_allowed_all=0b111110111111011111111111111111111111
bit_mask =(0b11,0b1110111,0b1110111,0b11111111,0b111,0b111,0b111,0b111)
bit_num  =(2,7,7,8,3,3,3,3)
data_mask=(0b1,0b111,0b111,0b111,0b11,0b11,0b11,0b11)
data_num =(17,14,11,8,6,4,2,0)

# 数据编码转用户可读编码
def data2userCode(data):
    rets=[]
    for d in data:
        data_pose, data_unclear=tuple(d['pose'])
        ret=''
        for dmask,dnum in zip(data_mask,data_num):
            if (data_unclear&(dmask<<dnum)):
                # 某位有抖动，用*标识
                ret+='*'
            else:
                ret+=str((data_pose&(dmask<<dnum))>>dnum)
        rets.append(ret)
    return rets
# 数据编码转允许集合编码
def data2allowCode(data):
    rets=[]
    for d in data:
        data_pose, data_unclear=tuple(d['pose'])
        pose=0
        unclear=0
        for dmask, bmask, dnum, bnum in zip(data_mask, bit_mask, data_num, bit_num):
            # 计算data_pose姿态编码对应集合编码
            mask0=dmask<<dnum
            pose=(pose<<bnum)|(1 << ((data_pose&mask0)>>dnum))
            # 计算data_unclear姿态抖动编码对应集合编码
            unclear<<=bnum
            if data_unclear&mask0:
                unclear|=bmask
        rets.append((pose, unclear))
    return rets
# 解析模糊基本手势编码
def parseBaseCode(base_str,base_str_len=None, i_str=None):
    i=0
    state=1
    bit_allowed=[]
    allowed=set()
    is_not =False
    def addAllowed(c):
        c = int(c)
        if c in bit_dict[i]:
            allowed.add(c)
        else:
            return False
        return True
    if not base_str_len:
        base_str_len=len(base_str)
        i_str=0
    while i_str<base_str_len and base_str[i_str]==' ':
        i_str+=1
    while i_str<base_str_len and i<8:
        c=base_str[i_str]
        i_str+=1
        if state==1:
            # 开始解析一位用户编码
            allowed = set()
            if c == '*':
                bit_allowed.append(set())
                i+=1
            elif c in bit_chars:
                if not addAllowed(c):
                    return None
                bit_allowed.append(allowed)
                i+=1
            elif c == '[':
                state=2
            else:
                return None
            allowed = set()
        elif state==2:
            # 解析到'['后第一个字符
            if c in bit_chars:
                if not addAllowed(c):
                    return None
                is_not =False
                state=3
            elif c == '^':
                is_not=True
                state=3
            else:
                return None
        elif state==3:
            # 解析'[]'中其他字符
            if c in bit_chars:
                if not addAllowed(c):
                    return None
            elif c == ']':
                if is_not:
                    # '[^]'为语法不允许格式
                    if allowed==set():
                        return None
                    bit_allowed.append(bit_dict[i]-allowed)
                else:
                    bit_allowed.append(allowed)
                state=1
                i+=1
            else:
                return None
        else:
            return None
    # 若未正常到达结束状态则报错
    if state != 1:
        return None
    # 若结尾不是分隔符则报错
    if i_str<base_str_len and not base_str[i_str] in (' ','(',')','|','@'):
        return None
    if i==8:
        # 若整好8位用户编码，计算允许集合编码
        bit_allowed_mask=0
        for i,allowed in enumerate(bit_allowed):
            allowed=list(allowed)
            allowed_mask=0
            if allowed:
                allowed[0]=1<<allowed[0]
                allowed_mask=reduce(lambda x,y:x|(1<<y), allowed)
            else:
                allowed_mask=bit_mask[i]
            bit_allowed_mask=(bit_allowed_mask<<bit_num[i])|allowed_mask
        return (bit_allowed_mask, bit_allowed, i_str)
    elif bit_allowed==[set()]:
        # 若只有一位'*'，则位全部允许集合编码
        return (bit_allowed_all, bit_allowed, i_str)
    else:
        return None
# 解析连续手势语句
def parseCombination(comb_str, actions):
    fsm_dict={1:[None]}
    fsm_state_max=0
    #状态转移集记录 开始状态 目标状态 是否并列 在fsm中(转移条件和目标)指针
    state_q=[[-1,0,False,[]]]

    i_str=0
    i_action=0
    comb_str_len=len(comb_str)
    actions_len =len(actions)
    while i_str<comb_str_len:
        c=comb_str[i_str]
        if c == ' ':
            i_str+=1
            continue
        elif c=='(':
            # 若是顺序关系，则更新当前层最后一个转移集记录，转移到下一状态
            # 否则，保持当前当前层最后一个转移集记录
            last_state=state_q[-1]
            # 判断是否并联
            if not last_state[2]:
                last_state[0]=last_state[1]
                fsm_state_max+=1
                last_state[1]=fsm_state_max
                last_state[3]=[]
            # 添加一个新层，起始状态是上一层最后转移集记录起始状态
            state_q.append([-1,last_state[0],False,[]])
        elif c==')':
            # 括号层数不匹配
            if len(state_q)<2:
                logging.error('ERROR:括号层数不匹配')
                return None
            last_state_in_bracket=state_q[-1]
            last_state_ot_bracket=state_q[-2]
            # 括号内最后一个是'(' 或 是'|'
            if last_state_in_bracket[0]==-1 or last_state_in_bracket[2]:
                logging.error("ERROR:括号内最后一个是'(' 或 是'|'")
                return None
            # 更新括号内最后转移的目标=上层的最后转移目标
            for trans in last_state_in_bracket[3]:
                trans[1]=last_state_ot_bracket[1]
            fsm_trans_in=fsm_dict.get(last_state_in_bracket[1])
            if fsm_trans_in:
                fsm_trans_ot=fsm_dict.get(last_state_ot_bracket[1])
                # 上层目标状态的动作=括号内最后转移目标状态动作
                if fsm_trans_ot:
                    fsm_trans_ot[0]=fsm_trans_in[0]
                else:
                    fsm_dict[last_state_ot_bracket[1]]=[fsm_trans_in[0]]
                # 删除括号内最后转移目标状态
                if len(fsm_trans_in)==1:
                    del fsm_dict[last_state_in_bracket[1]]
            # 剩余括号内最后转移作为上层最后转移
            last_state_ot_bracket[3].extend(last_state_in_bracket[3])
            state_q.pop()
            last_state_ot_bracket[2]=False
        elif c=='|':
            # 继续并列关系
            last_state=state_q[-1]
            # 最后一个是开头 或 '(' 或 '|' 报错
            if last_state[0]==-1 or last_state[2]:
                logging.error("ERROR:最后一个是开头 或 '(' 或 '|'")
                return None
            last_state[2]=True
        elif c=='@':
            # 在前一个目标状态加入动作
            last_state=state_q[-1]
            # 最后一个是开头 或 '(' 或 '|' 报错
            if last_state[0]==-1 or last_state[2]:
                return None
            fsm_trans=fsm_dict.get(last_state[1])
            if i_action>=actions_len:
                logging.error("ERROR:@动作参数不足")
                return None
            if fsm_trans:
                # 已经有动作，报错
                if fsm_trans[0]:
                    logging.error("ERROR:@前状态已经有动作")
                    return None
                fsm_trans[0]=actions[i_action]
            else:
                fsm_dict[last_state[1]]=[actions[i_action]]
            i_action+=1
        else:
            ret=parseBaseCode(comb_str,comb_str_len,i_str)
            if not ret:
                logging.error("ERROR:出现未知符号")
                return None
            last_state=state_q[-1]
            # 若是顺序关系，则更新当前层最后一个转移集记录，转移到下一状态
            # 否则，保持当前当前层最后一个转移集记录
            if not last_state[2]:
                last_state[0]=last_state[1]
                fsm_state_max+=1
                last_state[1]=fsm_state_max
                last_state[3]=[]
            fsm_trans=fsm_dict.get(last_state[0])
            # 将(状态转移条件和目标)加入fsm并记录到转移集记录
            last_trans=[ret[0],last_state[1]]
            last_state[3].append(last_trans)
            if fsm_trans:
                fsm_trans.append(last_trans)
            else:
                fsm_dict[last_state[0]]=[None,last_trans]
            # 默认结束并列关系
            last_state[2]=False
            i_str=ret[2]-1
        i_str+=1
    else:
        # 若结束时括号未匹配 或 最后是 '|' 或 最后是开头
        if len(state_q)!=1 or state_q[0][2] or state_q[0][0]==-1:
            logging.error("ERROR:结束时括号未匹配 或 最后是 '|' 或 最后是开头")
            return None
        if not state_q[0][1] in fsm_dict:
            fsm_dict[state_q[0][1]]=[None]
    return fsm_dict
# 解析多个手势
def parseGestures(gestures):
    fsms=[]
    fail=[]
    for i, g in enumerate(gestures):
        res = parseCombination(g[0],g[1])
        if res:
            fsms.append(res)
        else:
            fail.append(i+1)
    return fsms, fail
# 黑盒测试解析模糊基本手势语句
def testParseBaseCode():
    # 最复杂模糊基本手势表达'*******[^03]'共12字符
    # 可用符号
    vali_char=['*','[',']','^','0','3','\n']
    # 排列组合
    def enum(n=12):
        if n==0:
            yield ''
            return None
        for c in vali_char:
            if c!='\n':
                for e in enum(n-1):
                    # print(e)
                    yield c+e
            else:
                yield ''
                return None
    # 遍历所有排列，保存正确表达式
    right=[]
    for in_str in enum(12):
        ret = parseBaseCode(in_str, None, 0)
        if ret:
            res="{:<20}{:>45b}\n".format(in_str, ret[0])
            print(res)
            right.append(res)
    with open("testParseBaseCode.txt","w") as f:
        f.writelines(right)
# 黑盒测试解析连续手势语句
def testParseCombination():
    # 最复杂连续手势表达'( * @ ) | ( * * ) @'共10字符
    # 可用符号
    vali_char=['* ','( ',') ','| ','@ ','\n']
    # 排列组合
    def enum(n=10):
        if n==0:
            yield ''
            return None
        for c in vali_char:
            if c!='\n':
                for e in enum(n-1):
                    # print(e)
                    yield c+e
            else:
                yield ''
                return None
    # 遍历所有排列，保存正确表达式
    right=[]
    for in_str in enum(8):
        ret = parseCombination(in_str, ["##1", "##2"])
        if ret:   
            res="{:<20}{}\n".format(in_str, ret)
            print(res)
            right.append(res)
    with open("testParseCombination.txt","w") as f:
        f.writelines(right)
# 黑盒测试数据转用户可读编码
def testData2UserCode():
    # 遍历所有可能的数据编码
    data_pose=[0, 0]
    data = {'pose': data_pose}
    right=[]
    for i in range(18):
        data_pose[1]=1<<i
        ret = data2userCode([data])
        if ret:
            res="{:0>18b} {:0>18b} {:>10}\n".format(data_pose[0], data_pose[1], ret[0])
            print(res)
            right.append(res)
    data_pose[1]=0
    for i in range(262144):
        data_pose[0]=i
        ret = data2userCode([data])
        if ret:
            res="{:0>18b} {:0>18b} {:>10}\n".format(data_pose[0], data_pose[1], ret[0])
            print(res)
            right.append(res)
    with open("testData2UserCode.txt","w") as f:
        f.writelines(right)
# 黑盒测试数据编码转允许集合编码
def testData2AllowCode():
    # 遍历所有可能的数据编码
    data_pose=[0, 0]
    data = {'pose': data_pose}
    right=[]
    for i in range(18):
        data_pose[1]=1<<i
        ret = data2allowCode([data])
        if ret:
            res="{:0>18b} {:0>18b} {:0>36b} {:0>36b}\n".format(data_pose[0], data_pose[1], ret[0][0], ret[0][1])
            print(res)
            right.append(res)
    data_pose[1]=0
    for i in range(262144):
        data_pose[0]=i
        ret = data2allowCode([data])
        if ret:
            res="{:0>18b} {:0>18b} {:0>36b} {:0>36b}\n".format(data_pose[0], data_pose[1], ret[0][0], ret[0][1])
            print(res)
            right.append(res)
    with open("testData2AllowCode.txt","w") as f:
        f.writelines(right)

if __name__ == '__main__':
    # 数据编码转用户编码测试
    # testData2UserCode()
    # 数据编码转集合编码测试
    # testData2AllowCode()
    # 模糊基本手势解析测试
    # testParseBaseCode()
    # 连续手势解析测试
    # testParseCombination()
    ret=parseCombination('(*1[04]01000 @ *1[04]00000 @)', ["## action_4\nprint('down')"])
    print(ret)